package tools

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/xuri/excelize/v2"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func GetMd5String(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))

}

// ComparePasswords 验证密码
func ComparePasswords(hashedPwd string, plainPwd string) bool {
	byteHash := GetMd5String(hashedPwd)

	if byteHash != plainPwd {
		return false
	}
	return true
}

// StructToMapReflect 结构体转为Map[string]interface{}
func StructToMapReflect(in interface{}, tagName string) (map[string]interface{}, error) {
	out := make(map[string]interface{})

	v := reflect.ValueOf(in)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Struct { // 非结构体返回错误提示
		return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
	}

	t := v.Type()
	// 遍历结构体字段
	// 指定tagName值为map中key;字段值为map中value
	for i := 0; i < v.NumField(); i++ {
		fi := t.Field(i)
		if tagValue := fi.Tag.Get(tagName); tagValue != "" {
			out[tagValue] = v.Field(i).Interface()
		}
	}
	return out, nil
}

// InArrStr 验证数组中某个值是否存在 string
func InArrStr(dataArr []string, str string) bool {
	for _, v := range dataArr {
		if v == str {
			return true
		}
	}

	return false
}

// ArrStr map转字符串
func ArrStr(arr map[string]string) string {
	a := ""
	for _, v := range arr {
		a += v + ","
	}
	if a == "" {
		return "null"
	}
	return a
}

// Mkdir 定义一个创建文件目录的方法
func Mkdir(basePath string) string {
	//	1.获取当前时间,并且格式化时间
	folderName := time.Now().Format("20060102")
	folderPath := filepath.Join(basePath, folderName)
	//使用mkdirall会创建多层级目录
	os.MkdirAll(folderPath, os.ModePerm)
	return folderPath
}

// GetDaysInYearByThisYear 年中的第几天
func GetDaysInYearByThisYear() int {
	now := time.Now()
	total := 0
	arr := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
	y, month, d := now.Date()
	m := int(month)
	for i := 0; i < m-1; i++ {
		total = total + arr[i]
	}
	if (y%400 == 0 || (y%4 == 0 && y%100 != 0)) && m > 2 {
		total = total + d + 1
	} else {
		total = total + d
	}
	return total
}

func PostJson(urlPath string, headers map[string]string, bodyMap map[string]any, method string) (map[string]any, error) {
	var info = make(map[string]any)
	param, err := json.Marshal(bodyMap)
	if err != nil {
		return info, err
	}
	req, err := http.NewRequest(method, urlPath, bytes.NewBuffer(param))
	if err != nil {
		return info, err
	}

	for key, header := range headers {
		req.Header.Set(key, header)
	}

	req.Header.Set("Content-Type", "application/json")
	var client = http.DefaultClient
	response, err := client.Do(req)
	if err != nil {
		return info, err
	}
	defer response.Body.Close()
	BodyData, err := io.ReadAll(response.Body)
	if err != nil {
		return info, err
	}
	//log.Println(string(BodyData))
	err = json.Unmarshal(BodyData, &info)
	if err != nil {
		return info, err
	}
	return info, nil
}

// GetConstellation 获取星座
func GetConstellation(t time.Time) string {
	gap := []int{20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22}
	star := []string{"摩羯座", "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"}
	if t.Day() < gap[t.Month()-1] {
		return star[t.Month()-1]
	} else {
		return star[t.Month()%12]
	}
}

// UnicodeEmojiDecode emoji表情解码
func UnicodeEmojiDecode(s string) string {
	//emoji表情的数据表达式
	re := regexp.MustCompile("\\[[\\\\u0-9a-zA-Z]+\\]")
	//提取emoji数据表达式
	reg := regexp.MustCompile("\\[\\\\u|]")
	src := re.FindAllString(s, -1)
	for i := 0; i < len(src); i++ {
		e := reg.ReplaceAllString(src[i], "")
		p, err := strconv.ParseInt(e, 16, 32)
		if err == nil {
			s = strings.Replace(s, src[i], string(rune(p)), -1)
		}
	}
	return s
}

// UnicodeEmojiCode emoji表情转换
func UnicodeEmojiCode(s string) string {
	ret := ""
	rs := []rune(s)
	for i := 0; i < len(rs); i++ {
		if len(string(rs[i])) == 4 {
			u := `[\u` + strconv.FormatInt(int64(rs[i]), 16) + `]`
			ret += u

		} else {
			ret += string(rs[i])
		}
	}
	return ret
}

// GetInterfaceToInt any 转 int
func GetInterfaceToInt(t1 any) int {
	var t2 int
	switch t1.(type) {
	case uint:
		t2 = int(t1.(uint))
		break
	case int8:
		t2 = int(t1.(int8))
		break
	case uint8:
		t2 = int(t1.(uint8))
		break
	case int16:
		t2 = int(t1.(int16))
		break
	case uint16:
		t2 = int(t1.(uint16))
		break
	case int32:
		t2 = int(t1.(int32))
		break
	case uint32:
		t2 = int(t1.(uint32))
		break
	case int64:
		t2 = int(t1.(int64))
		break
	case uint64:
		t2 = int(t1.(uint64))
		break
	case float32:
		t2 = int(t1.(float32))
		break
	case float64:
		t2 = int(t1.(float64))
		break
	case string:
		t2, _ = strconv.Atoi(t1.(string))
		if t2 == 0 && len(t1.(string)) > 0 {
			f, _ := strconv.ParseFloat(t1.(string), 64)
			t2 = int(f)
		}
		break
	case nil:
		t2 = 0
		break
	case json.Number:
		t3, _ := t1.(json.Number).Int64()
		t2 = int(t3)
		break
	default:
		t2 = t1.(int)
		break
	}
	return t2
}

// GetTissueId 获取组织id
func GetTissueId(ids string) int {

	idsArr := strings.Split(ids, ",")
	id := 0
	for _, v := range idsArr {
		id, _ = strconv.Atoi(v)
	}
	return id
}

// GenerateXlsx 导出Excel
func GenerateXlsx(data [][]any, fileName string) (string, error) {
	f := excelize.NewFile()
	sheetName := "Sheet1"
	// 创建一个工作表
	err := f.SetSheetName("Sheet1", sheetName)

	if err != nil {
		return "", err
	}

	err = f.SetColWidth("Sheet1", "A", "A", 8)
	err = f.SetColWidth("Sheet1", "B", "E", 28)
	err = f.SetColWidth("Sheet1", "F", "G", 8)
	err = f.SetColWidth("Sheet1", "G", "M", 20)
	if err != nil {
		return "", err
	}
	for k, row := range data {
		startCell, err := excelize.JoinCellName("A", k+1)
		if err != nil {
			return "", err
		}
		err = f.SetSheetRow(sheetName, startCell, &row)
		if err != nil {
			return "", err
		}

	}

	t := time.Now()
	fileName = "public/excel/" + fileName + "-" + t.Format("20060102150405") + ".xlsx"
	// 根据指定路径保存文件
	err = f.SaveAs(fileName)
	if err != nil {
		return "", err

	}
	return fileName, nil
}

// GenerateXlsx2 导出Excel
func GenerateXlsx2(data [][]any, fileName string) (string, error) {
	f := excelize.NewFile()
	sheetName := "Sheet1"
	// 创建一个工作表
	err := f.SetSheetName("Sheet1", sheetName)

	if err != nil {
		return "", err
	}

	err = f.SetColWidth("Sheet1", "A", "A", 8)
	err = f.SetColWidth("Sheet1", "B", "G", 15)
	err = f.SetColWidth("Sheet1", "H", "I", 6)
	err = f.SetColWidth("Sheet1", "J", "N", 10)
	err = f.SetColWidth("Sheet1", "O", "W", 27)
	if err != nil {
		return "", err
	}
	for k, row := range data {
		startCell, err := excelize.JoinCellName("A", k+1)
		if err != nil {
			return "", err
		}
		err = f.SetSheetRow(sheetName, startCell, &row)
		if err != nil {
			return "", err
		}

	}

	t := time.Now()
	fileName = "public/excel/" + fileName + "-" + t.Format("20060102150405") + ".xlsx"
	// 根据指定路径保存文件
	err = f.SaveAs(fileName)
	if err != nil {
		return "", err

	}
	return fileName, nil
}

// AdminExportInvoiceExcel 导出Excel
func AdminExportInvoiceExcel(data [][]any) (string, error) {
	f := excelize.NewFile()
	sheetName := "发票信息"
	// 创建一个工作表
	err := f.SetSheetName("Sheet1", sheetName)
	if err != nil {
		return "", err
	}

	err = f.SetColWidth(sheetName, "A", "A", 20)
	err = f.SetColWidth(sheetName, "B", "B", 10)
	err = f.SetColWidth(sheetName, "C", "H", 30)
	err = f.SetColWidth(sheetName, "I", "L", 17)
	err = f.SetColWidth(sheetName, "M", "N", 30)
	err = f.SetColWidth(sheetName, "O", "T", 10)
	err = f.SetColWidth(sheetName, "U", "AJ", 17)
	if err != nil {
		return "", err
	}
	for k, row := range data {
		startCell, err := excelize.JoinCellName("A", k+1)
		if err != nil {
			return "", err
		}

		err = f.SetSheetRow(sheetName, startCell, &row)
		if err != nil {
			return "", err
		}

	}

	//sheetName = "作废发票"
	//// 创建一个工作表
	//_, err = f.NewSheet(sheetName)
	//if err != nil {
	//	return "", err
	//}
	//
	//err = f.SetColWidth(sheetName, "A", "B", 10)
	//err = f.SetColWidth(sheetName, "C", "H", 30)
	//err = f.SetColWidth(sheetName, "I", "L", 17)
	//err = f.SetColWidth(sheetName, "M", "N", 30)
	//err = f.SetColWidth(sheetName, "O", "T", 10)
	//err = f.SetColWidth(sheetName, "U", "AJ", 17)
	//if err != nil {
	//	return "", err
	//}
	//for k, row := range kaiPiaoData {
	//	startCell, err := excelize.JoinCellName("A", k+1)
	//	if err != nil {
	//		return "", err
	//	}
	//	err = f.SetSheetRow(sheetName, startCell, &row)
	//	if err != nil {
	//		return "", err
	//	}
	//
	//}

	t := time.Now()
	fileName := "public/excel/发票信息-" + t.Format("20060102150405") + ".xlsx"
	// 根据指定路径保存文件
	err = f.SaveAs(fileName)
	if err != nil {
		return "", err

	}
	return fileName, nil
}

// CheckType 判断数据类型 转换string
func CheckType(i any) (string, error) {
	switch i.(type) {
	case int:
		return strconv.Itoa(i.(int)), nil
	case string:
		return i.(string), nil
	case float64:
		return strconv.Itoa(int(i.(float64))), nil
	}

	return "", errors.New("未定义的类型")
}

// StringToTime 日期 字符串转时间
func StringToTime(toBeCharge string) time.Time {
	timeLayout := "2006-01-02 15:04:05"                             //转化所需模板
	loc, _ := time.LoadLocation("Local")                            //重要：获取时区
	theTime, _ := time.ParseInLocation(timeLayout, toBeCharge, loc) //使用模板在对应时区转化为time.time类型
	return theTime
}

// IsMobile 判断是否 pc端 还是移动端
func IsMobile(c *gin.Context) bool {
	// 这里可以使用更复杂的用户代理匹配逻辑
	userAgent := c.Request.UserAgent()
	if userAgent != "" && (userAgent[:7] == "iPhone " || userAgent[:7] == "Android" || userAgent[:6] == "iPad " || userAgent[:4] == "iOS " || userAgent[:4] == "Mac " || userAgent[:6] == "Windows") {
		return true
	}
	return false
}

func StringArrToIntArr(s []string) []int {
	var intArr []int
	for _, v := range s {
		i, _ := strconv.Atoi(v)
		intArr = append(intArr, i)
	}
	return intArr
}
